#!/usr/bin/env python
# encoding: utf-8
'''
Created on Jun 10, 2011

@author: apple
'''
import time
import cPickle
import pymongo
import types

class MongoModel(object):
    lasterror = None
    
    def __init__(self, db , pk='_id', cache=None , expire=0):
        self.db = db
        self.pk = pk
        self.cache = cache if getattr(cache, 'set',None) else None
        self.expire = expire
                
    @classmethod
    def getlasterror(self):
        return self.lasterror
    
    def get(self, **kv):
        _id  = kv.get(self.pk,'')
        if _id:
            data = self.__get_cache__(_id)
            if data:
                return data
        result = self.db.find(kv)
        if result.count() == 0:
            return None
        self.__set_cache__(result[0])
        return result[0]
    
    def filter(self,spec,skip=0,limit=10,sortfield=None,sortmethod=pymongo.DESCENDING):
        result = self.db.find(spec,skip=skip,limit=limit) if not sortfield else self.db.find(spec,skip=skip,limit=limit).sort(sortfield,sortmethod)
        return result
    
    def save(self,**kv):
        if not kv.has_key(self.pk):
            self.lasterror = 'No primary key. at save'
            return False
        kv['timestamp'] = kv.get('timestamp',None) or time.time()
        try:
            self.db.insert(kv,safe=True)
            self.__set_cache__(kv)
            return True
        except:
            self.lasterror = 'Insert into mongodb fail.at save'
            return False
    
    def insert(self,*docs):
        return [self.save(d) for d in docs]
    
    def update(self,spec,doc,**kv):
        if not kv:
            kv['safe'] = {}
        _id = spec.get(self.pk,None)
        doc.pop(self.pk,None)
        kv['safe'] = True
        if _id:
            data = self.__get_cache__(_id)
            if data:
                data.update(doc.items())
                self.__set_cache__(data)
        result = self.db.update(spec,{'$set':doc},**kv)
        return result['updatedExisting']
    
    def remove(self,_id):
        return self.db.remove(_id,safe=True).get('n')
        
    def __len__(self):
        return self.db.find().count()
        
    def __set_cache__(self,item,**kv):
        if not self.cache or not item.has_key(self.pk):
            self.lasterror = 'No primary key or no cache server.at __set_cache__'
            return None
        data = cPickle.dumps(item)
        if type(item[self.pk]) != types.StringType:
            item[self.pk] = item[self.pk].encode('utf-8')
        return self.cache.set(item[self.pk],data,**kv)
        
    def __get_cache__(self,key):
        if not self.cache :
            self.lasterror = 'No cache server.at __get_cache__'
            return None
        if type(key) != types.StringType:
            key = key.encode('utf-8')
        rawdata = self.cache.get(key)
        try:
            return cPickle.loads(rawdata)
        except:
            self.lasterror = 'cPickle.loads fail,invalid data format.at __get_cache__'
            return None
        
    def __cache_all__(self,items):
        for i in items:
            self.__set_cache__(i)
